﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.Text;

namespace SmartMathLibrary.Management.Monitoring
{
    /// <summary>
    /// Occurs when the message limit of the monitor get exceed.
    /// </summary>
    public delegate void PhysicalMemoryLimitExceedanceEventHandler(double memoryUsage);

    /// <summary>
    /// This class provides methods to monitor the physical system memory. All values
    /// have to be declared in the dimension MegaByte.
    /// </summary>
    [Serializable]
    public class PhysicalMemoryMonitor : AbstractMemoryMonitor, IDisposable
    {
        /// <summary>
        /// The performance counter, which get the memory usage.
        /// </summary>
        private readonly PerformanceCounter performanceCounter;

        /// <summary>
        /// Occurs when the message limit get exceed.
        /// </summary>
        public event PhysicalMemoryLimitExceedanceEventHandler PhysicalMemoryUsageEvent;

        /// <summary>
        /// Initializes a new instance of the <see cref="AbstractMemoryMonitor"/> class.
        /// </summary>
        /// <param name="messageLimit">The message limit. If the message limit get exceed the monitor 
        /// automatically throws a LimitExceedanceEvent.</param>
        /// <param name="samplingRate">The sampling rate.</param>
        public PhysicalMemoryMonitor(double messageLimit, double samplingRate)
            : base(messageLimit, samplingRate)
        {
            this.performanceCounter = new PerformanceCounter("Memory", "Available MBytes");
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PhysicalMemoryMonitor"/> class.
        /// </summary>
        /// <param name="messageLimit">The message limit. If the message limit get exceed the monitor 
        /// automatically throws a LimitExceedanceEvent.</param>
        /// <param name="samplingRate">The sampling rate.</param>
        /// <param name="startMonitoring">if set to <c>true</c> the monitor will start automatically
        /// otherwise, it must be started by method Start().</param>
        public PhysicalMemoryMonitor(double messageLimit, double samplingRate, bool startMonitoring)
            : base(messageLimit, samplingRate, startMonitoring)
        {
            this.performanceCounter = new PerformanceCounter("Memory", "Available MBytes");
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PhysicalMemoryMonitor"/> class.
        /// </summary>
        /// <param name="messageLimit">The message limit. If the message limit get exceed the monitor 
        /// automatically throws a LimitExceedanceEvent.</param>
        /// <param name="samplingRate">The sampling rate.</param>
        /// <param name="monitoringPriority">The monitoring priority.</param>
        public PhysicalMemoryMonitor(double messageLimit, double samplingRate, MonitoringPriority monitoringPriority)
            : base(messageLimit, samplingRate, monitoringPriority)
        {
            this.performanceCounter = new PerformanceCounter("Memory", "Available MBytes");
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PhysicalMemoryMonitor"/> class.
        /// </summary>
        /// <param name="messageLimit">The message limit. If the message limit get exceed the monitor 
        /// automatically throws a LimitExceedanceEvent.</param>
        /// <param name="samplingRate">The sampling rate.</param>
        /// <param name="monitoringPriority">The monitoring priority.</param>
        /// <param name="startMonitoring">if set to <c>true</c> the monitor will start automatically
        /// otherwise, it must be started by method Start().</param>
        public PhysicalMemoryMonitor(double messageLimit, double samplingRate, MonitoringPriority monitoringPriority,
                                     bool startMonitoring)
            : base(messageLimit, samplingRate, monitoringPriority, startMonitoring)
        {
            this.performanceCounter = new PerformanceCounter("Memory", "Available MBytes");
        }

        /// <summary>
        /// Gets the physical memory usage in MegaBytes.
        /// </summary>
        /// <value>The physical memory usage in MegaBytes.</value>
        public double PhysicalMemoryUsage
        {
            get { return this.performanceCounter.NextValue(); }
        }

        /// <summary>
        /// Handles the Tick event of the samplingTimer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected override void SamplingTimer_Tick(object sender, EventArgs e)
        {
            double usage = this.PhysicalMemoryUsage;

            if (usage >= base.MessageLimit)
            {
                this.PhysicalMemoryUsageEvent(usage);
            }
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public virtual void Dispose()
        {
            this.Dispose(true);
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        public virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.performanceCounter.Dispose();
                GC.SuppressFinalize(this);
            }
        }
    }
}